#include "Nano100Series.h"
#include <string.h>
#include "Common.h"
#include "Config.h"

#define DATA_FLASH_BASE        				0xF000
#define DATA_FLASH_CONFIG_NORMAL_BASE		(DATA_FLASH_BASE + FMC_FLASH_PAGE_SIZE * 1)
#define DATA_FLASH_CONFIG_BACKUP_BASE		(DATA_FLASH_BASE + FMC_FLASH_PAGE_SIZE * 2)

struct sys_config_t sys_config;

static struct sys_config_t sys_config_default = {
	1,					// version

	// version 1
	80,					// brightness
};

uint32_t SysConfigCalcSum(uint32_t *addr, uint32_t count)
{
	uint32_t sum = 0;
	
	while (count--) {
		sum += *(addr++);
	}
	return sum;
}

void SysConfigSetBase(uint32_t base)
{
    uint32_t au32Config[2];
	uint32_t df_enable_mask = 1;
	uint32_t need_update = 0;

	SYS_UnlockReg();
	FMC_Open();

    if (FMC_ReadConfig(au32Config, 2) < 0) {
		return;
    }

	LogDebug("FMC user config[0] : 0x%08X\n", au32Config[0]);
	LogDebug("FMC user config[1] : 0x%08X\n", au32Config[1]);

	if (au32Config[0] & df_enable_mask) {
		LogDebug("data flash is disabled, now enable it\n");
		au32Config[0] &= (~df_enable_mask);
		need_update = 1;
	} else {
		LogDebug("data flash is already enabled\n");
	}

	if (au32Config[1] == base) {
		LogDebug("DFBA(0x%08X) is same as base(0x%08X)\n", au32Config[1], base);
	} else {
		LogDebug("DFBA(0x%08X) is different from base(0x%08X), now set it\n", au32Config[1], base);
		au32Config[1] = base;
		need_update = 1;
	}

	if (need_update) {
		FMC_ENABLE_CFG_UPDATE();
		if (FMC_WriteConfig(au32Config, 2) < 0) {
			LogDebug("write user config failed\n ");
		} else {
			LogDebug("write user config ok\n ");
			SYS->IPRST_CTL1 = SYS_IPRST_CTL1_CHIP_RST_Msk;
		}
		FMC_DISABLE_CFG_UPDATE();
	}
}

void SysConfigReadRaw(uint32_t base, struct sys_config_t *sys_config, uint32_t *sum)
{
	int count;
	uint32_t *buf;

	buf = (uint32_t *)sys_config;
	SYS_UnlockReg();
	FMC_Open();
	for (count = 0; count < (sizeof(struct sys_config_t) / 4); count++) {
		*(buf++) = FMC_Read(base + count * 4);
	}
	*sum = FMC_Read(base + sizeof(struct sys_config_t));
	FMC_Close();
	SYS_LockReg();
}

void SysConfigWriteRaw(uint32_t base, struct sys_config_t *sys_config, uint32_t sum)
{
	int count;
	uint32_t *buf;

	buf = (uint32_t *)sys_config;
	SYS_UnlockReg();
	FMC_Open();
	FMC_Erase(base);
	for (count = 0; count < (sizeof(struct sys_config_t) / 4); count++) {
		FMC_Write(base + count * 4, *(buf++));
	}
	FMC_Write(base + sizeof(struct sys_config_t), sum);
	FMC_Close();
	SYS_LockReg();
}

void SysConfigLoad(struct sys_config_t *sys_config)
{
	uint32_t normal_sum_read;
	uint32_t normal_sum_calc;
	uint32_t backup_sum_read;
	uint32_t backup_sum_calc;
	uint32_t default_sum_calc;
	struct sys_config_t sys_config_normal;
	struct sys_config_t sys_config_backup;

	// read normal config
	SysConfigReadRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &sys_config_normal, &normal_sum_read);
	normal_sum_calc = SysConfigCalcSum((uint32_t *)&sys_config_normal, sizeof(struct sys_config_t) / 4);

	// read backup config
	SysConfigReadRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &sys_config_backup, &backup_sum_read);
	backup_sum_calc = SysConfigCalcSum((uint32_t *)&sys_config_backup, sizeof(struct sys_config_t) / 4);

	if (normal_sum_calc == normal_sum_read) {
		// normal config is ok, 
		if (backup_sum_calc == backup_sum_read) {
			// backup config is ok return
			memcpy(sys_config, &sys_config_normal, sizeof(struct sys_config_t));
			return;
		} else {
			// backup config is wrong, save backup config from normal config
			SysConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &sys_config_normal, normal_sum_calc);
			// return
			memcpy(sys_config, &sys_config_normal, sizeof(struct sys_config_t));
			return;
		}
	} else {
		// normal config is wrong, try backup config
		if (backup_sum_calc == backup_sum_read) {
			// backup config is ok, save normal backup from backup config
			SysConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &sys_config_backup, backup_sum_calc);
			// return
			memcpy(sys_config, &sys_config_backup, sizeof(struct sys_config_t));
			return;
		} else {
			// backup config is wrong, save normal config and backup config from default config
			default_sum_calc = SysConfigCalcSum((uint32_t *)&sys_config_default, sizeof(struct sys_config_t) / 4);
			SysConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &sys_config_default, default_sum_calc);
			SysConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &sys_config_default, default_sum_calc);
			memcpy(sys_config, &sys_config_default, sizeof(struct sys_config_t));
			return;
		}
	}
}

void SysConfigStore(struct sys_config_t *sys_config)
{
	uint32_t sum;
	
	sum = SysConfigCalcSum((uint32_t *)sys_config, sizeof(struct sys_config_t) / 4);
	SysConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, sys_config, sum);
	SysConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, sys_config, sum);
}

void SysConfigDump(struct sys_config_t *sys_config)
{
	LogDebug("Dump sys_config begin\n");
	LogDebug("sys_config.version : %u\n", sys_config->version);
	LogDebug("sys_config.brightness : %u\n", sys_config->brightness);
	LogDebug("Dump sys_config end\n");
}
void SysConfigInit(void)
{
	SysConfigSetBase(DATA_FLASH_BASE);
}
